home *** CD-ROM | disk | FTP | other *** search
- //
- // HKCrashTrap version 1.0
- //
- // Based on ObjectError
- // Original class by:
- // Bill Bumgarner, Andrew Stone, Mike Morton, and Julie Zalenski
- //
- // Modifications by:
- // Ivo Rothschild (ivo@hasc.ca)
- //
- // A class that poses as object and does crash-reporting.
- // Catches terminating signals (ie seg faults, bus errors)
- // and fatal Objective-C runtime errors and writes a backtrace
- // out to the console using some shady hacks. This could be modified
- // to write backtrace to a file, mail message, etc... if desired.
- //
- //
- // You may freely copy, distribute, and reuse the code in this class.
- //
- // NO WARRANTY:
- // ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
- // IS HEREBY DISCLAIMED. IN NO EVENT WILL THE AFOREMENTIONED PARTIES BE LIABLE
- // FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
- // DAMAGES ARISING OUT OF THE USE OF OR INABILITY TO USE THIS CODE.
- //
-
- #import "HKCrashTrap.h"
- #import <appkit/Application.h>
- #import <appkit/nextstd.h>
- #import <appkit/appkit.h>
- #import <strings.h>
- #import <stdio.h>
- #import <sys/signal.h>
- #import <objc/objc-runtime.h>
-
- typedef struct _sig {
- int number;
- BOOL isOn;
- char *message;
- } SignalItem;
-
- typedef struct _type {
- char encoding;
- char *format;
- char *name;
- } ObjcType;
-
- extern SignalItem signals[];
- extern ObjcType encodings[];
-
- static void handle_signal(int signal);
- static BOOL ignoreCrashes;
- static BOOL continueAfterError;
-
- #define FREED_OBJECT_NAME "<FreedObject>"
-
- static void(*_originalError)(Object *anObject, const char *format, va_list ap);
- /*
- ** Define an error function that dumps a backtrace.
- */
- static void _objectError(Object *anObject, const char *format, va_list ap)
- {
- char buf[BUFSIZ];
-
- vsprintf(buf,format,ap);
- _error = _originalError;
- [HKCrashTrap dumpBacktrace:buf];
- _error = _objectError;
- if (!continueAfterError)
- {
- abort();
- }
- [HKCrashTrap tryToContinue];
- return;
- }
-
- @implementation HKCrashTrap
-
- + setup
- // Initialize everything and poseAs the Object class.
- {
- _originalError = _error;
- _error = _objectError;
-
- [[self class] poseAs:[Object class]];
- [[self class] setSignalHandler:handle_signal];
- ignoreCrashes=NO;
- continueAfterError=NO;
- return self;
- }
-
- + setContinueAfterError:(BOOL)flag;
- {
- continueAfterError = flag;
- return self;
- }
-
- + (BOOL)continueAfterError;
- {
- return continueAfterError;
- }
-
- + setSignalHandler:(void (*)())handler
- {
- SignalItem *cur;
-
- for (cur=signals; cur->number; cur++) {
- if (cur->isOn) {
- signal(cur->number, handler);
- }
- }
- return self;
- }
-
- + resumeHandlingCrashes
- {
- [[self class] setSignalHandler:handle_signal];
- ignoreCrashes=NO;
- return self;
- }
-
- + stopHandlingCrashes
- {
- [[self class] setSignalHandler:(void (*)())SIG_DFL];
- ignoreCrashes=YES;
- return self;
- }
-
- // ASSUMPTION: The layout of a stack frame for a method invocation is:
- // fp+0 bytes: calling frame
- // fp+4 bytes: calling pc
- // fp+8 bytes: self
- // fp+12 bytes: selector for method invoked
- // fp+16 bytes: first argument
- //
- // ASSUMPTION: The layout of a stack frame for a function invocation is:
- // fp+0 bytes: calling frame
- // fp+4 bytes: calling pc
- // fp+8 bytes: first argument
- //
- // Clearly these are shady assumptions, however we're already
- // in the process of crashing, so what harm can be done?
-
- #define MAX_FUNCTION_ARGS 4 // Print at most four args to a function
-
- + printFunctionFromFP:(void *)framePointer
- {
- char buffer[256];
- char line[1024];
- void *argStart;
- long argNum; // Index into arguments;
-
- sprintf(line, "function (");
- argStart = framePointer + 8;
- for (argNum=0; argNum<MAX_FUNCTION_ARGS; argNum++) {
- sprintf(buffer, "%s0x%06lx", ((argNum != 0)?", ":""),
- *(((unsigned long *)argStart)+argNum));
- strcat(line, buffer);
- }
- strcat(line, ")");
- NXLogError("%s\n", line);
-
- return self;
- }
-
- #define IS_CLASS(object) ([object class] == object)
-
- + printMethodFromFP:(void *)framePointer
- {
- char buffer[256];
- char line[1024];
- SEL selector;
- id object, class, classIsa;
- Method m;
- BOOL isClassMethod;
-
- object = *(id *)(framePointer+8); // receiver is 8 bytes from fp
- selector = *(SEL *)(framePointer+12); // selector is 12 bytes from fp
-
- classIsa = nil;
- object_getInstanceVariable(object, "isa", (void **)&class);
- if (class)
- object_getInstanceVariable(class, "isa", (void **)&classIsa);
- if (!classIsa)
- {
- // Freed object.
- sprintf(line, "%c[%s %s",'?',FREED_OBJECT_NAME, sel_getName(selector));
- }
- else
- {
- isClassMethod = IS_CLASS(object);
-
- sprintf(line, "%c[%s %s", (isClassMethod?'+':'-'),
- object_getClassName(object), sel_getName(selector));
-
- m = (isClassMethod ? class_getClassMethod([object class], selector)
- : class_getInstanceMethod([object class], selector));
- if (m) {
- void *argStart = (framePointer+8);
- int argNum, numArgs, offset;
- char *type;
-
- numArgs = method_getNumberOfArguments(m);
- argNum = 2; // Skip the first two args which are self and _cmd
- while (argNum<numArgs) {
- ObjcType *cur;
-
- method_getArgumentInfo(m, argNum, &type, &offset);
- for (cur=encodings; cur->encoding; cur++) {
- // Find the ObjcType
- if (cur->encoding == type[0]) {
- sprintf(buffer, " :(%s)", cur->name);
- strcat(line, buffer);
- sprintf(buffer, cur->format,
- *(long *)(argStart+offset));
- strcat(line, buffer);
- break;
- }
- }
- argNum++;
- }
- } else {
- strcat(line, " Unknown method");
- }
- }
-
- strcat(line, "]");
- NXLogError("%s\n", line);
-
- return self;
- }
-
- #define MAX_FRAMES 50
-
- + printBacktrace
- {
- void *framePointer; // pointer to current frame
- unsigned int frameCount; // counter for number of frames printed
-
- [self stopHandlingCrashes]; // Try to avoid re-entry problems
-
- // Start the frame pointer off at our frame
- framePointer = ((void *) &self)-8;
- frameCount=0;
-
- // Assume that a whole lotta frames means either (a) we're trashed or
- // (b) we're in a recursive deathtrap. In the latter case, we've
- // probably got enough info to see a whole cycle.
-
- while (frameCount<MAX_FRAMES && framePointer) {
- // If this frame is a method call we'll have a valid
- // selector at (fp+12).
- if (sel_isMapped(*(SEL *)(framePointer+12))) {
- [self printMethodFromFP:framePointer];
- } else {
- [self printFunctionFromFP:framePointer];
- }
- framePointer = (void *)*(long *)framePointer; // go up one frame
- }
- return self;
- }
-
- + dumpBacktrace:(const char *)message
- {
- NXLogError("%s\n", message);
- NXLogError("Here's the stack frame Backtrace:\n");
- [self printBacktrace];
- return self;
- }
-
- static void handle_signal(int signal)
- {
- const char *msg=NULL;
- char buf[256];
- SignalItem *cur;
-
- msg = "Unrecognized signal";
- for (cur = signals; cur->number; cur++) {
- if (cur->number==signal) {
- msg = cur->message;
- break;
- }
- }
- sprintf(buf, "Caught signal #%d: \"%s\"", signal, msg);
- [HKCrashTrap dumpBacktrace:buf];
- if (!continueAfterError) {
- exit(1);
- }
- [HKCrashTrap tryToContinue];
- }
-
- - error:(const char *)aString, ...
- {
- va_list ap;
- char buffer[1024];
-
- va_start(ap, aString);
- vsprintf(buffer, aString, ap);
- va_end(ap);
-
- if (!ignoreCrashes) [[self class] dumpBacktrace:buffer];
- if (continueAfterError)
- {
- [[self class] tryToContinue];
- return self;
- }
- return [super error:buffer];
- }
-
- + error:(const char *)aString, ...
- {
- va_list ap;
- char buffer[1024];
-
- va_start(ap, aString);
- vsprintf(buffer, aString, ap);
- va_end(ap);
-
- if (!ignoreCrashes) [[self class] dumpBacktrace:buffer];
- if (continueAfterError)
- {
- [self tryToContinue];
- return self;
- }
- return [super error:buffer];
- }
-
- + tryToContinue;
- {
- int mask;
-
- NXLogError("Trying to continue...\n");
- NXRunAlertPanel("Error Alert!","An internal error has occurred. Try to save work, then quit and restart the application.","Continue",NULL,NULL);
-
- mask = 0;
- sigsetmask(mask); // unblock all signals...
- [self resumeHandlingCrashes];
-
- [NXApp abortModal];
- return self;
- }
-
- #define ON 1
- #define OFF 0
-
- SignalItem signals[] = {
- {SIGHUP, OFF, "Hangup"},
- {SIGINT, OFF, "Interrupt"},
- {SIGQUIT, ON, "Quit"},
- {SIGILL, ON, "Illegal instruction"},
- {SIGTRAP, ON, "Trace trap"},
- {SIGIOT, ON, "IOT instruction"},
- {SIGEMT, ON, "EMT instruction"},
- {SIGFPE, ON, "Floating point exception"},
- {SIGKILL, OFF, "Kill"},
- {SIGBUS, ON, "Bus error"},
- {SIGSEGV, ON, "Segmentation violation"},
- {SIGSYS, ON, "Bad argument to system call"},
- {SIGPIPE, OFF, "Write on a pipe with no one to read it"},
- {SIGALRM, OFF, "Alarm clock"},
- {SIGTERM, OFF, "Software termination"},
- {SIGURG, OFF, "Urgent condition present on socket"},
- {SIGSTOP, OFF, "Stop"},
- {SIGTSTP, OFF, "Stop signal generated from keyboard"},
- {SIGCONT, OFF, "Continue after stop"},
- {SIGCHLD, OFF, "Child status changed"},
- {SIGTTIN, OFF, "Background read attempted from control terminal"},
- {SIGTTOU, OFF, "Background write attempted to control terminal"},
- {SIGIO, OFF, "I/O is possible on a descriptor"},
- {SIGXCPU, OFF, "CPU time limit is exceeded"},
- {SIGXFSZ, OFF, "File size limit exceeded"},
- {SIGVTALRM, OFF, "Virtual timer alarm"},
- {SIGPROF, OFF, "Profiling timer alarm"},
- {SIGWINCH, OFF, "Window size change"},
- {SIGUSR1, OFF, "User defined signal 1"},
- {SIGUSR2, OFF, "User defined signal 2"},
- {0},
- };
-
- ObjcType encodings[] = {
- {_C_ID, "0x%06lx", "id"},
- {_C_CLASS, "0x%06lx", "Class"},
- {_C_SEL, "%s", "SEL"},
- {_C_CHR, "%c", "char"},
- {_C_UCHR, "%c", "unsigned char"},
- {_C_SHT, "%s", "short"},
- {_C_USHT, "%c", "unsigned short"},
- {_C_INT, "%d", "int"},
- {_C_UINT, "%d", "unsigned int"},
- {_C_LNG, "%l", "long"},
- {_C_ULNG, "%l", "unsigned long"},
- {_C_FLT, "%f", "float"},
- {_C_DBL, "%f", "double"},
- {_C_VOID, "0x%06lx", "void"},
- {_C_PTR, "0x%06lx", "ptr"},
- {_C_CHARPTR, "%s", "char *"},
- {_C_STRUCT_B, "%x", "struct"},
- {0, "0x%06lx", "unknown type"},
- };
-
- @end
-
-
-